package orbit

import (
	"strings"
	"time"

	"gddgame.cc/galaxy/asteroid/cluster"
	"gddgame.cc/galaxy/binocle"
	"gddgame.cc/galaxy/binocle/module/socket"
	"gddgame.cc/galaxy/common/feature/orbit"
	"gddgame.cc/galaxy/common/feature/orbit/chess_protocol"
	"gddgame.cc/galaxy/project/orbit/orbit/chess"
	"gddgame.cc/galaxy/project/orbit/orbit/common"
	"gddgame.cc/galaxy/satellite/formula/comet"
	"gddgame.cc/galaxy/server/composer"
	"gddgame.cc/galaxy/server/web"
	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/serialize"
)

type chessManager struct {
	builder composer.Builder
	binocle.Service

	layerService  *chess.LayerService
	singleService *chess.SingleService
	control       *common.ControlService
}

func (cm *chessManager) Connect(socket *comet.Socket) error {
	openId := ""
	//var store def.Store
	var container cluster.Container
	// 全局设置
	c := chess.NewClient()
	c.Bind(socket)
	if err := socket.Set("client", c); err != nil {
		return err
	}

	socket.UpdateAuth(func(authority interface{}) {
		a := authority.(*auth)
		openId = a.openId
		//store = cm.StoreWith(a)
		container = cm.ClusterWith(a)

		socket.Println("chess auth", container)
		syncNS := a.nsMap["chess"]
		if syncNS == nil {
			syncNS = &ns{settings: map[string]interface{}{}}
		}
		if rate, ok := syncNS.settings["rate"]; ok {
			c.FrameRate = time.Duration(utils.ToInt32(rate))
		}
		if identity, ok := syncNS.settings["identity"]; ok {
			c.Identity = utils.ToInt(identity)
		}
	})
	clients := map[string]*cluster.Client{}
	socket.Dispatch(func(event comet.DE, packet comet.Packet) {
		b := serialize.GetBag()
		if err := b.Unmarshal(packet.ReadBytes()); err != nil {
			b.Release()
			return
		}
		switch event {
		case chess.SwitchMap:
			mapNoId := b.ReadString(0)
			if mapNoId == c.MapId {
				socket.Println("switch map keep", c.MapId)
				return
			}
			c.MapId = mapNoId
			c.Level = chess.DefaultLevel
			c.InitLevel = false
			c.Size = orbit.LocationCM.DecodeLocation(b.ReadBytes(1))
			socket.Println("switch map", c.MapId)
			if len(clients) > 0 {
				for _, client := range clients {
					client.Leave(socket, openId)
				}
				clients = map[string]*cluster.Client{}
			}
			socket.Send(chess.OnSwitchMap, utils.ToBytes(c.MapId))
		case chess.SwitchLevel:
			level := b.ReadByte(0)
			levelPoint := orbit.Point(utils.ToInt16(level))
			if c.Level == levelPoint && c.InitLevel {
				socket.Println("switch level keep", c.Level)
				socket.Send(chess.OnSwitchLevel, []byte{level})
				return
			}
			c.Level = levelPoint
			c.InitLevel = true
			socket.Println("switch level", c.Level)
			if len(clients) > 0 {
				for _, client := range clients {
					client.Leave(socket, openId)
				}
				clients = map[string]*cluster.Client{}
			}
			socket.Send(chess.OnSwitchLevel, []byte{level})
		case chess.Register:
			location := orbit.LocationCM.DecodeLocation(b.ReadBytes(0))
			block := orbit.LocationCM.CenterBlockWithLocation(c.Size.Multiple(c.Level), c.Size.Multiple(c.Level), location)
			orbit.LocationCM.ReleaseLocation(location)
			sub := orbit.GetSubWithBlock(block)
			client := cm.layerService.GetWithSub(container, c.MapId, sub, c.Level)
			_, ok := clients[client.ID()]
			if ok {
				orbit.LocationCM.ReleaseBlock(block)
				socket.Println("register keep", block, c.Level)
				return
			}
			client.Join(socket, openId)
			clients[client.ID()] = client
			socket.Println("register", block, c.Level)
			orbit.LocationCM.ReleaseBlock(block)
		case chess.UnRegister:
			location := orbit.LocationCM.DecodeLocation(b.ReadBytes(0))
			block := orbit.LocationCM.CenterBlockWithLocation(c.Size.Multiple(c.Level), c.Size.Multiple(c.Level), location)
			orbit.LocationCM.ReleaseLocation(location)
			sub := orbit.GetSubWithBlock(block)
			nodeId := cm.layerService.GetNodeId(c.MapId, sub, c.Level)
			if v, ok := clients[nodeId]; ok {
				v.Leave(socket, openId)
				delete(clients, nodeId)
				socket.Println("unregister", block, c.Level)
			} else {
				socket.Println("unregister miss", block, c.Level)
			}
			orbit.LocationCM.ReleaseBlock(block)
		}

		b.Release()
	})
	socket.Disconnected(func(reason string) {
		for _, v := range clients {
			v.Leave(socket, openId)
		}
		c.Release()
	})
	return nil
}

func (cm *chessManager) Disconnect(socket *comet.Socket, reason string) error {
	return nil
}

/*
 聊天室逻辑
*/
func ChessHandler(builder composer.Builder, config *Config, env *orbit.Environment, controlService *common.ControlService) comet.Interface {
	var agent binocle.Service
	builder.Factory("agent", &agent)
	c := &chessManager{
		builder: builder,
		Service: agent,
	}
	c.control = controlService

	chessBlockService := &chess.BlockService{
		Logger: builder,
		GetSocketClient: func(socket *comet.Socket) *chess.Client {
			var result *chess.Client
			err := socket.Get("client", &result)
			if err == nil {
				return result
			} else {
				return nil
			}
		},
	}
	chessLayerService := &chess.LayerService{
		Logger:  builder,
		Service: agent,
		Block:   chessBlockService,
	}
	c.layerService = chessLayerService
	agent.Cluster().RegisterBalance(chessLayerService)

	chessSingleService := &chess.SingleService{
		Logger:  builder,
		Service: agent,
		Block:   chessBlockService,
	}
	c.singleService = chessSingleService
	agent.Cluster().RegisterBalance(chessSingleService)

	agent.ListenDo("chess/control", func(request socket.Request) {
		var data chess_protocol.ControlData
		if err := request.Data(&data); err != nil {
			request.Debugln("[ Chess ] control err", err)
			return
		}
		controlClient := common.NewControlClient(c.ClusterWith(request), "chess")
		controlClient.Emit().Data(request.Serialize()).Send(chess.Receive)
		//request.Debugln("[ Chess ] control ", data)
	})
	agent.ListenDo("chess/receive", func(request socket.Request) {
		var data chess_protocol.BlockData
		if err := request.Data(&data); err != nil {
			request.Debugln("[ Chess ] layer receive err", err)
			return
		}
		switch data.Mode {
		case chess_protocol.LayerChessMode:
			client := c.layerService.GetWithSub(c.ClusterWith(request), data.MapNoId, data.Sub, 1)
			client.Emit().Data(request.Serialize()).Send(chess.Receive)
		default:
			client := c.singleService.GetWithSub(c.ClusterWith(request), data.MapNoId, data.Sub, 1)
			client.Emit().Data(request.Serialize()).Send(chess.Receive)
		}
	})
	agent.ListenDo("chess/close", func(request socket.Request) {
		var data chess_protocol.CloseData

		if err := request.Data(&data); err != nil {
			request.Debugln("[ Chess ] close err", err)
			return
		}
		id := ""
		switch data.Mode {
		case chess_protocol.LayerChessMode:
			id = c.layerService.ID()
		default:
			id = c.singleService.ID()
		}
		agent.ClusterWith(request).Balance().ServiceRange(id, func(node cluster.Node, master bool) bool {
			// block master 包含areaId
			token := node.Token()
			if !master || !strings.Contains(token, data.MapNoId) {
				//request.Debugln("[ Chess ] close pass", token, master)
				return true
			}
			_ = node.Close()
			request.Infoln("[ Chess ] close", token, master)
			return true
		})
		request.Debugln("[ Chess ] close ", data)
	})

	return c
}

func ChessAppHandler(builder composer.Builder, router web.Router, config *Config) {
	var agent binocle.Service
	builder.Factory("agent", &agent)
	agent.MountNode(func(node cluster.Node) {

	})
}

func ChessManageHandler(builder composer.Builder, router web.Router, config *Config) {
	//s := builder.Feature("system")
	//s.MountNode(func(node cluster.Node) {
	//
	//})

	router.Get("/topic")
}
